<?php


namespace App\Helper;

use App\Contracts\LoggerInterface;
use App\Event\LoggerPanic;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\Formatter\FormatterInterface;
use Hyperf\Logger\LoggerFactory;
use Hyperf\Utils\ApplicationContext;
use Hyperf\Utils\Arr;
use Hyperf\Utils\Context;
use Psr\Container\ContainerInterface;
use Psr\EventDispatcher\EventDispatcherInterface;

/**
 * Class Logger
 * @package App\Helper
 *
 * @method debug($e, $name = null, $group = null) 详细的调试信息。
 * @method info($e, $name = null, $group = null) 200 有意义的事件，比如用户登录、SQL日志
 * @method notice($e, $name = null, $group = null) 250 正常但是值得注意的事件
 * @method warning($e, $name = null, $group = null) 300 异常事件，但是并不是错误。比如使用了废弃了的API，错误地使用了一个API，以及其他不希望发生但是并非必要的错误
 * @method error($e, $name = null, $group = null) 400 运行时的错误，不需要立即注意到，但是需要被专门记录并监控到
 * @method critical($e, $name = null, $group = null) 500 边界条件/危笃场景。比如应用组件不可用了，未预料到的异常
 * @method alert($e, $name = null, $group = null) 550 必须立即采取行动。比如整个网站都挂了，数据库不可用了等。这种情况应该发送短信警报，并把你叫醒
 * @method emergency($e, $name = null, $group = null) 600 紧急请求：系统不可用了
 */
class Logger implements LoggerInterface
{
    private $container;


    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
        // 配置文件或配置中心里读取调试模式默认值
        Context::set('logger.is_debug', $container->get(ConfigInterface::class)->get('logger.debug_mode', true));
    }

    /**
     * 强制开启调试
     * @return $this
     */
    public function forceDebug()
    {
        Context::set('logger.is_debug', true);
        return $this;
    }

    /**
     * 是否调试模式
     * @return bool
     */
    public function isDebug(): bool
    {
        return Context::get('logger.is_debug', false);
    }


    public function __call($logType, $arguments)
    {
        if (!$this->isDebug() && !$this->isFearful($logType)) {
            return;
        }

        if (!in_array($logType, ['debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency'])) {
            return;
        }

        go(function () use ($logType, $arguments) {
            $container = ApplicationContext::getContainer();
            $formatter = $container->get(FormatterInterface::class);
            try {
                $e = Arr::get($arguments, 0);
                $name = Arr::get($arguments, 1, $logType);
                $group = Arr::get($arguments, 2, 'default');
                if ($e instanceof \Throwable) {
                    $message = $formatter->format($e);
                } else {
                    $message = $e;
                }
                if ($this->isFearful($logType)) {
                    // 严重问题,触发事件方便添加其它处理
                    $this->container->get(EventDispatcherInterface::class)->dispatch(new LoggerPanic($logType, $message, $name, $group));
                }

                $container->get(LoggerFactory::class)->get($name, $group)->{$logType}($message);
                $container->get(StdoutLoggerInterface::class)->{$logType}($message);
            } catch (\Throwable $e) {
                $container->get(StdoutLoggerInterface::class)->error($formatter->format($e));
            }
        });
    }

    /**
     * 是否严重的
     *
     * @param $logType
     * @return bool
     */
    private function isFearful($logType): bool
    {
        return in_array($logType, ['error', 'critical', 'alert', 'emergency']);
    }
}